package com.kexio.file.storage.impl;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.UUID;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import com.kexio.file.dto.FileChunk;
import com.kexio.file.dto.FileUploadRequest;
import com.kexio.file.dto.FileUploadResult;
import com.kexio.file.storage.StorageMetadata;
import com.kexio.file.storage.StorageStrategy;

/**
 * 本地存储策略实现
 * 
 * @author kexio
 */
@Component
public class LocalStorageStrategy implements StorageStrategy {
    
    private static final Logger log = LoggerFactory.getLogger(LocalStorageStrategy.class);
    
    @Value("${kexio.file.local.basePath:/data/files}")
    private String basePath;
    
    @Value("${kexio.file.local.urlPrefix:http://localhost:8080/api/files}")
    private String urlPrefix;
    
    @Override
    public String getStorageType() {
        return "local";
    }

    @Override
    public FileUploadResult upload(FileUploadRequest request, InputStream inputStream) {
        try {
            // 生成存储路径
            String storageKey = generateStorageKey(request);
            Path targetPath = Paths.get(basePath, storageKey);
            
            // 确保父目录存在
            Files.createDirectories(targetPath.getParent());
            
            // 保存文件
            Files.copy(inputStream, targetPath, StandardCopyOption.REPLACE_EXISTING);
            
            // 构建访问URL
            String accessUrl = urlPrefix + "/" + storageKey;
            
            log.info("文件上传成功: {} -> {}", request.getOriginalFilename(), storageKey);
            
            FileUploadResult result = new FileUploadResult();
            result.setStorageKey(storageKey);
            result.setAccessUrl(accessUrl);
            result.setStorageType(getStorageType());
            result.setFileSize(request.getFileSize());
            result.setFileHash(request.getFileHash());
            
            return result;
            
        } catch (IOException e) {
            log.error("本地文件上传失败: {}", request.getOriginalFilename(), e);
            throw new RuntimeException("文件上传失败", e);
        }
    }

    @Override
    public InputStream download(String storageKey) {
        try {
            Path filePath = Paths.get(basePath, storageKey);
            if (!Files.exists(filePath)) {
                throw new FileNotFoundException("文件不存在: " + storageKey);
            }
            return new FileInputStream(filePath.toFile());
        } catch (IOException e) {
            log.error("本地文件下载失败: {}", storageKey, e);
            throw new RuntimeException("文件下载失败", e);
        }
    }

    @Override
    public boolean delete(String storageKey) {
        try {
            Path filePath = Paths.get(basePath, storageKey);
            return Files.deleteIfExists(filePath);
        } catch (IOException e) {
            log.error("本地文件删除失败: {}", storageKey, e);
            return false;
        }
    }

    @Override
    public boolean exists(String storageKey) {
        Path filePath = Paths.get(basePath, storageKey);
        return Files.exists(filePath);
    }

    @Override
    public String getAccessUrl(String storageKey, int expireSeconds) {
        // 本地存储不支持临时URL，返回永久URL
        return urlPrefix + "/" + storageKey;
    }

    @Override
    public String initMultipartUpload(FileUploadRequest request) {
        // 为分片上传生成唯一ID
        String uploadId = UUID.randomUUID().toString();
        
        // 创建分片临时目录
        Path tempDir = Paths.get(basePath, "temp", uploadId);
        try {
            Files.createDirectories(tempDir);
            log.info("初始化分片上传: uploadId={}, file={}", uploadId, request.getOriginalFilename());
            return uploadId;
        } catch (IOException e) {
            log.error("初始化分片上传失败: {}", request.getOriginalFilename(), e);
            throw new RuntimeException("初始化分片上传失败", e);
        }
    }

    @Override
    public String uploadChunk(String uploadId, FileChunk chunk) {
        try {
            // 分片文件路径
            String chunkId = uploadId + "_" + chunk.getChunkNumber();
            Path chunkPath = Paths.get(basePath, "temp", uploadId, "chunk_" + chunk.getChunkNumber());
            
            // 保存分片文件
            Files.copy(chunk.getInputStream(), chunkPath, StandardCopyOption.REPLACE_EXISTING);
            
            log.debug("分片上传成功: uploadId={}, chunkNumber={}", uploadId, chunk.getChunkNumber());
            return chunkId;
            
        } catch (IOException e) {
            log.error("分片上传失败: uploadId={}, chunkNumber={}", uploadId, chunk.getChunkNumber(), e);
            throw new RuntimeException("分片上传失败", e);
        }
    }

    @Override
    public FileUploadResult mergeChunks(String uploadId, List<String> chunkIds) {
        try {
            Path tempDir = Paths.get(basePath, "temp", uploadId);
            
            // 生成最终文件路径
            String storageKey = generateStorageKey(uploadId);
            Path targetPath = Paths.get(basePath, storageKey);
            Files.createDirectories(targetPath.getParent());
            
            // 合并分片
            try (var outputStream = Files.newOutputStream(targetPath)) {
                for (int i = 1; i <= chunkIds.size(); i++) {
                    Path chunkPath = tempDir.resolve("chunk_" + i);
                    if (Files.exists(chunkPath)) {
                        Files.copy(chunkPath, outputStream);
                    }
                }
            }
            
            // 清理临时文件
            cleanupTempFiles(tempDir);
            
            // 构建结果
            FileUploadResult result = new FileUploadResult();
            result.setStorageKey(storageKey);
            result.setAccessUrl(urlPrefix + "/" + storageKey);
            result.setStorageType(getStorageType());
            result.setFileSize(Files.size(targetPath));
            
            log.info("分片合并完成: uploadId={}, storageKey={}", uploadId, storageKey);
            return result;
            
        } catch (IOException e) {
            log.error("分片合并失败: uploadId={}", uploadId, e);
            throw new RuntimeException("分片合并失败", e);
        }
    }

    @Override
    public boolean abortMultipartUpload(String uploadId) {
        try {
            Path tempDir = Paths.get(basePath, "temp", uploadId);
            cleanupTempFiles(tempDir);
            log.info("取消分片上传: uploadId={}", uploadId);
            return true;
        } catch (Exception e) {
            log.error("取消分片上传失败: uploadId={}", uploadId, e);
            return false;
        }
    }

    @Override
    public boolean copy(String sourceKey, String targetKey) {
        try {
            Path sourcePath = Paths.get(basePath, sourceKey);
            Path targetPath = Paths.get(basePath, targetKey);
            
            if (!Files.exists(sourcePath)) {
                return false;
            }
            
            Files.createDirectories(targetPath.getParent());
            Files.copy(sourcePath, targetPath, StandardCopyOption.REPLACE_EXISTING);
            
            log.info("文件复制成功: {} -> {}", sourceKey, targetKey);
            return true;
            
        } catch (IOException e) {
            log.error("文件复制失败: {} -> {}", sourceKey, targetKey, e);
            return false;
        }
    }

    @Override
    public StorageMetadata getMetadata(String storageKey) {
        try {
            Path filePath = Paths.get(basePath, storageKey);
            if (!Files.exists(filePath)) {
                return null;
            }
            
            StorageMetadata metadata = new StorageMetadata();
            metadata.setSize(Files.size(filePath));
            metadata.setContentType(Files.probeContentType(filePath));
            metadata.setLastModified(LocalDateTime.now()); // 本地文件系统限制，使用当前时间
            
            return metadata;
            
        } catch (IOException e) {
            log.error("获取文件元信息失败: {}", storageKey, e);
            return null;
        }
    }
    
    /**
     * 生成存储键
     */
    private String generateStorageKey(FileUploadRequest request) {
        String datePrefix = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd"));
        String filename = UUID.randomUUID().toString();
        
        if (request.getOriginalFilename() != null) {
            String extension = getFileExtension(request.getOriginalFilename());
            if (extension != null) {
                filename += "." + extension;
            }
        }
        
        return datePrefix + "/" + filename;
    }
    
    private String generateStorageKey(String uploadId) {
        String datePrefix = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd"));
        return datePrefix + "/" + uploadId;
    }
    
    /**
     * 获取文件扩展名
     */
    private String getFileExtension(String filename) {
        if (filename == null || filename.isEmpty()) {
            return null;
        }
        int dotIndex = filename.lastIndexOf('.');
        if (dotIndex > 0 && dotIndex < filename.length() - 1) {
            return filename.substring(dotIndex + 1).toLowerCase();
        }
        return null;
    }
    
    /**
     * 清理临时文件
     */
    private void cleanupTempFiles(Path tempDir) throws IOException {
        if (Files.exists(tempDir)) {
            Files.walk(tempDir)
                .sorted((p1, p2) -> p2.compareTo(p1)) // 先删除文件，再删除目录
                .forEach(path -> {
                    try {
                        Files.delete(path);
                    } catch (IOException e) {
                        log.warn("删除临时文件失败: {}", path, e);
                    }
                });
        }
    }
}
